
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

/**
 * @author lichao9
 * @date 2021年05月20日 11:11
 * 基于flowable JAVA API方式 控制台输入输出的请假流程案例
 * 测试基于: flowable 6.6 版本
 * 测试遇到的几个问题:
 * 1.mysql驱动版本过高出现列过长异常,切换 5.1.45 版本
 * 2.mysql需要采用支持事物处理 Inndb引擎
 */
public class HolidayRequest {
    public static void main(String[] args){
        //1.代码配置方式: 引擎配置,数据库
        //2.自动加载 classpath:/flowable.cfg.xml 方式创建
        //参考: http://doc.miguren.cn/bpmn/#_configuration
        ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
//                .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1")
                .setJdbcUrl("jdbc:mysql://localhost:3306/flow?characterEncoding=UTF-8&serverTimezone=UTC&useUnicode=true&useSSL=false&nullCatalogMeansCurrent=true")
                .setJdbcUsername("root")
                .setJdbcPassword("root")
//                .setJdbcDriver("org.h2.Driver")
                .setJdbcDriver("com.mysql.jdbc.Driver")
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); //表自动更新
        //创建独立的流程引擎
        ProcessEngine processEngine = cfg.buildProcessEngine();
        //使用配置方式加载后可以直接
//        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine()

        //创建流程定义仓库服务
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //创建流程定义的发布对象,并且发布一个 holiday-request.bpmn20.xml 的定义文件,即发布一个流程
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("holiday-request.bpmn20.xml")
                .deploy();

        //根据流程ID 查询流程定义是否已经发布到流程引擎
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        System.out.println("发现已发布的流程 : " + processDefinition.getName());

        //根据流程定义 ,启动一个流程实例
        //1.注入流程变量

        //要启动流程实例，需要提供一些初始化流程变量。一般来说，可以通过呈现给用户的表单，
        //或者在流程由其他系统自动触发时通过REST API，来获取这些变量。
        //在这个例子里，我们简化为使用java.util.Scanner类在命令行输入一些数据
        //在控制台输入流程变量
        Scanner scanner= new Scanner(System.in);

        System.out.println("你是?");
        String employee = scanner.nextLine();

        System.out.println("你需要请多长的假期?");
        Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

        System.out.println("请假理由是?");
        String description = scanner.nextLine();

        //根据流程定义 ,启动一个流程实例
        //2.启动实例

        //我们使用RuntimeService启动一个流程实例。收集的数据作为一个java.util.Map实例传递，
        // 其中的键就是之后用于获取变量的标识符。这个流程实例使用key启动。
        // 这个key就是BPMN 2.0 XML文件中设置的id属性，在这个例子里是holidayRequest。
        // 这个值:<process id="holidayRequest" name="Holiday Request" isExecutable="true">
        //（请注意：除了使用key之外，在后面你还会看到有很多其他方式启动一个流程实例）

        RuntimeService runtimeService = processEngine.getRuntimeService();

        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("employee", employee); //姓名
        variables.put("nrOfHolidays", nrOfHolidays); //天数
        variables.put("description", description); //理由
        ProcessInstance processInstance =
                runtimeService.startProcessInstanceByKey("holidayRequest", variables);


        //查询任务列表
        //1.根据组查询任务列表
        TaskService taskService = processEngine.getTaskService();
        List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list(); //经理组
        System.out.println("你有 " + tasks.size() + " 个待处理业务:");
        for (int i=0; i<tasks.size(); i++) {
            System.out.println((i+1) + ") " + tasks.get(i).getName());
        }

        //根据任务ID查询任务
        System.out.println("你审批通过那个业务?");
        int taskIndex = Integer.valueOf(scanner.nextLine());
        Task task = tasks.get(taskIndex - 1);
        Map<String, Object> processVariables = taskService.getVariables(task.getId());
        System.out.println(processVariables.get("employee") + " 向你审请 " +
                processVariables.get("nrOfHolidays") + " 天假期 , 是否同意?");

        //判断条件 approved true / false ?
        boolean approved = scanner.nextLine().toLowerCase().equals("y"); //y 同意 , n 拒绝
        //具体看定义 holiday-request.bpmn20.xml 定义节点
        //同意将会调用 org.flowable.CallExternalSystemDelegate
        //拒绝将会调用 org.flowable.SendRejectionMail
        variables = new HashMap<String, Object>();
        variables.put("approved", approved);
        taskService.complete(task.getId(), variables);


        //历史数据查询服务
        HistoryService historyService = processEngine.getHistoryService();
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        .processInstanceId(processInstance.getId())  //制定流程定义的ID
                        .finished() //查询已经结束的任务
                        .orderByHistoricActivityInstanceEndTime().asc()  //根据结束时间排序
                        .list();

        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " took "
                    + activity.getDurationInMillis() + " milliseconds");
        }
    }
}
